package ling.learn.syntax;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.FileUtils;

public class Syntax2AntlrConverter {
  private Map<String, String> operator2Ids = new ConcurrentHashMap<>();
  private Map<String, Map<String, String>> rulesTypeMap = new ConcurrentHashMap<>();

  public Syntax2AntlrConverter() {
    String rulePrefix = "OP_";
    operator2Ids.put(":=", rulePrefix + "ASSIGN");
    operator2Ids.put("<>", rulePrefix + "NOT_EQUAL1");
    operator2Ids.put("!=", rulePrefix + "NOT_EQUAL2");
    operator2Ids.put("<=", rulePrefix + "LE");
    operator2Ids.put(">=", rulePrefix + "GE");
    operator2Ids.put("(.", rulePrefix + "LBRACK2");
    operator2Ids.put(".)", rulePrefix + "RBRACK2");

    operator2Ids.put("+", rulePrefix + "PLUS");
    operator2Ids.put("-", rulePrefix + "MINUS");
    operator2Ids.put("*", rulePrefix + "STAR");
    operator2Ids.put("/", rulePrefix + "SLASH");
    operator2Ids.put(",", rulePrefix + "COMMA");
    operator2Ids.put(";", rulePrefix + "SEMI");
    operator2Ids.put(":", rulePrefix + "COLON");
    operator2Ids.put("=", rulePrefix + "EQUAL");
    operator2Ids.put("<", rulePrefix + "LT");
    operator2Ids.put(">", rulePrefix + "GT");
    operator2Ids.put("(", rulePrefix + "LPAREN");
    operator2Ids.put(")", rulePrefix + "RPAREN");
    operator2Ids.put("[", rulePrefix + "LBRACK");
    operator2Ids.put("]", rulePrefix + "RBRACK");
    operator2Ids.put("{", rulePrefix + "LCURLY");
    operator2Ids.put("}", rulePrefix + "RCURLY");
    operator2Ids.put("\\", rulePrefix + "YEN");
    operator2Ids.put(".", rulePrefix + "DOT");
    operator2Ids.put("$", rulePrefix + "DOLLAR");
    operator2Ids.put("|", rulePrefix + "OR");
    operator2Ids.put("&", rulePrefix + "AMBER");
    operator2Ids.put("~", rulePrefix + "NOT");
    operator2Ids.put("?", rulePrefix + "HELP");
  }

  public void addRule(String type, String id, String text) {
    rulesTypeMap.computeIfAbsent(type, k -> new ConcurrentHashMap<>()).computeIfAbsent(id,
        k -> text);
  }

  public void execute(String grammarName) throws IOException {
    rulesTypeMap.clear();

    String dirPath = "resource\\" + grammarName + "_Syntax\\";
    Collection<File> files = FileUtils.listFiles(new File(dirPath), new String[] {"csv"}, true);
    for (File file : files) {
      String ruleId = file.getName().substring(0, file.getName().length() - 4);
      List<SyntaxRecord> records = SyntaxReader.read(file);
      SyntaxGraph graph = convert(records);

      List<List<String>> paths = graph.findAllPaths("000000");
      boolean first = true;
      for (List<String> path : paths) {
        System.out.println(graph.formatPath(path));
        if (first) {
          addRule("statement", ruleId, graph.formatPath(path) + "(OP_DOT)? EOL");
          first = false;
        }
      }
    }
    addOperatorRules();
    AntlrGrammarWriter writer = new AntlrGrammarWriter();
    writer.write(dirPath, grammarName, rulesTypeMap);
  }

  private SyntaxGraph convert(List<SyntaxRecord> records) {
    SyntaxGraph graph = new SyntaxGraph();
    for (SyntaxRecord record : records) {
      convert(record, graph);
    }
    return graph;
  }

  private void convert(SyntaxRecord record, SyntaxGraph graph) {
    String keyWordParm = record.keyWordParm;
    String[] keyWordParms = keyWordParm.split("\\$");
    String from = record.statusNo;
    String to = record.nextNo;
    String id = "";
    String text = "";
    String idPrefix = "";
    switch (record.keyKBN) {
      case "0":
        idPrefix = "KW_";
        for (String parm : keyWordParms) {
          id = idPrefix + parm;
          text = "'" + parm + "'";
          addRule("keyword", id, text);
          graph.addEdge(from, to, id);
        }
        id = "";
        text = "";
        break;
      case "1":
        idPrefix = "OP_";
        keyWordParms = keyWordParm.split("\\$");
        for (String parm : keyWordParms) {
          id = idPrefix + parm;
          id = operator2Ids.getOrDefault(parm, id);
          // text = "'" + parm.replace("\\", "\\\\") + "'";
          // addRule("operator", id, text);
          graph.addEdge(from, to, id);
        }
        id = "";
        text = "";
        break;
      case "2":
        if (!record.keyWordParm.isEmpty()) {
          idPrefix = "ws_";
          id = idPrefix + keyWordParm.toLowerCase();
          text = "wordList";
        } else {
          graph.addEdge(from, to, "");
        }
        break;
      case "6":
        idPrefix = "wd_";
        id = idPrefix + keyWordParm.toLowerCase();
        text = "word";
        break;
      case "14":
        idPrefix = "no_";
        id = idPrefix + keyWordParm.toLowerCase();
        text = "numericLiteral";
        break;
      case "22":
        idPrefix = "st_";
        id = idPrefix + keyWordParm.toLowerCase();
        text = "nonnumericLiteral";
        break;
      case "54":
        idPrefix = "id_";
        id = idPrefix + keyWordParm.toLowerCase();
        text = "Identifier";
        break;
      default:
        break;
    }
    if (!id.isEmpty()) {
      addRule("other", id, text);
      graph.addEdge(from, to, id);
    }
  }

  private void addOperatorRules() {
    for (Map.Entry<String, String> entry : operator2Ids.entrySet()) {
      addRule("operator", entry.getValue(), "'" + entry.getKey().replace("\\", "\\\\") + "'");
    }
  }
}
